Harden PubSub replay, Action, and DTLS security (OPC 10000-14 / RFC 9147)#4044
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
This PR hardens PubSub replay/nonce handling, Action request security semantics, and DTLS peer/replay protections to align with OPC 10000-14 and RFC 9147 guidance.
Changes:
- Scopes UADP replay windows by
(PublisherId, WriterGroupId, SecurityTokenId)while keeping nonce uniqueness global per token/key, and avoids replay-state mutation on unsigned frames. - Serializes DTLS record sequence/crypto use, adds epoch sequence exhaustion handling, and pins CID-less DTLS associations to the authenticated peer endpoint.
- Makes “allow unsecured Action” an opt-in per responder instead of a connection-wide switch, with new regression/integration tests.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Opc.Ua.PubSub.Udp.Tests/Dtls/DtlsRecordProtectionTests.cs | Adds parallel AEAD sealing/opening test and sequence exhaustion regression. |
| tests/Opc.Ua.PubSub.Udp.Tests/Dtls/DtlsHandshakeContextTests.cs | Extends handshake tests and in-memory channel to validate authenticated-peer pinning. |
| tests/Opc.Ua.PubSub.Udp.Tests/Dtls/DtlsDatagramTransportPeerTests.cs | New integration test ensuring pinned DTLS peer isn’t redirected by spoofed sources. |
| tests/Opc.Ua.PubSub.Tests/Security/UadpSecurityWrapperReplayTests.cs | New tests for scoped replay state and required-mode behavior. |
| tests/Opc.Ua.PubSub.Tests/Security/SecurityTokenWindowTests.cs | Adds nonce-reuse regression across publisher/writer-group scopes. |
| tests/Opc.Ua.PubSub.Tests/Connections/PubSubConnectionPrivateMethodTests.cs | Updates private-method test to pass publisher/writer-group scoping args. |
| tests/Opc.Ua.PubSub.Tests/Connections/PubSubConnectionCoverageTests.cs | Adds coverage for per-responder unsecured Action opt-in isolation. |
| src/Opc.Ua.PubSub/Security/UadpSecurityWrapper.cs | Adds scoped unwrap overload, early required-mode rejection, and scoped replay acceptance paths. |
| src/Opc.Ua.PubSub/Security/SecurityTokenWindow.cs | Implements scoped sequence windows and global nonce tracking; adds scoped interface implementation. |
| src/Opc.Ua.PubSub/Security/ISecurityTokenWindow.cs | Clarifies legacy contract and introduces internal scoped window interface. |
| src/Opc.Ua.PubSub/Connections/PubSubConnection.cs | Moves unsecured Action policy to per-responder; passes publisher/writer-group to unwrap. |
| src/Opc.Ua.PubSub.Udp/UdpDatagramTransport.cs | Adds ability to pin authenticated remote endpoint; makes “last seen peer” tracking configurable. |
| src/Opc.Ua.PubSub.Udp/Dtls/IDtlsDatagramChannel.cs | Adds optional authenticated-peer pinning channel capability. |
| src/Opc.Ua.PubSub.Udp/Dtls/DtlsRecordProtection.cs | Serializes sealing/opening/dispose, reconstructs sequence safely, rejects sequence exhaustion. |
| src/Opc.Ua.PubSub.Udp/Dtls/DtlsHandshakeContext.cs | Filters handshake frames by expected source and pins authenticated peer on completion. |
| src/Opc.Ua.PubSub.Udp/Dtls/DtlsDatagramTransport.cs | Pins CID-less DTLS association to authenticated peer; filters spoofed sources before replay/crypto. |
Defer replay-state commits until authentication completes, bound nonce tracking with a Bloom filter, and harden DTLS tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #4044 +/- ##
==========================================
+ Coverage 73.55% 73.62% +0.06%
==========================================
Files 1345 1345
Lines 180036 180294 +258
Branches 31677 31707 +30
==========================================
+ Hits 132427 132739 +312
+ Misses 36911 36840 -71
- Partials 10698 10715 +17
🚀 New features to boost your workflow:
|
Raise changed-line patch coverage of the replay/DTLS hardening changes to ~100% by covering the newly introduced branches with deterministic NUnit tests (no reflection, minimal mocking): - SecurityTokenWindow: scoped replay-state cleanup on token retirement while other tokens' scopes are retained. - UadpSecurityWrapper: legacy (non-scoped) token-window fallback and unsigned scoped frames that must not consume authenticated replay state. - PubSubConnection: per-responder unsecured JSON Action rejection and opportunistic unwrap of a secured frame when security is not required. - DtlsDatagramTransport: authenticated-peer null and conflicting-rebind guards, plus close clearing the pinned peer. - DtlsRecordProtection: initial record-sequence validation and idempotent dispose. - UdpDatagramTransport: connected send client rejects a null pin and ignores redirect attempts to a different endpoint. New tests pass on net10.0 (Release) and, where DTLS is supported, on net48. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Description
This draft is the focused PubSub/UDP/DTLS security split from #4021.
The changes:
(PublisherId, WriterGroupId, SecurityTokenId)while retaining global per-key nonce uniqueness across all publisher and writer-group scopes;These changes implement the replay and nonce requirements in OPC 10000-14 §7.2.4.4.2 and the DTLS security considerations in RFC 9147 §11.
Validation
git diff --check origin/master...HEAD— passed.dotnet test tests\Opc.Ua.PubSub.Tests\Opc.Ua.PubSub.Tests.csproj --configuration Release --framework net10.0 -p:CustomTestTarget=net10.0— 1,324 passed, 1 skipped.dotnet test tests\Opc.Ua.PubSub.Udp.Tests\Opc.Ua.PubSub.Udp.Tests.csproj --configuration Release --framework net10.0 -p:CustomTestTarget=net10.0— 227 passed.dotnet test tests\Opc.Ua.PubSub.Tests\Opc.Ua.PubSub.Tests.csproj --configuration Release --framework net48 -p:CustomTestTarget=net48— 1,324 passed, 1 skipped.dotnet test tests\Opc.Ua.PubSub.Udp.Tests\Opc.Ua.PubSub.Udp.Tests.csproj --configuration Release --framework net48 -p:CustomTestTarget=net48— 175 passed, 4 skipped.Direct regression coverage includes forged-record replay poisoning, scoped sequence windows and nonce reuse, concurrent sequence serialization, sequence exhaustion, per-responder unsecured Action isolation, and authenticated-peer pinning.
Related Issues
Checklist